#include <iostream>
#include <pthread.h>
#include <cstdio>
#include <cstring>
#include <unistd.h>

using namespace std;

// int 票数计数器
// 临界资源
int tickets = 10000; // 临界资源，可能会因为共同访问，可能会造成数据不一致问题。

// 定义一个全局的锁
pthread_mutex_t mutex;

void* getTickets(void* args)
{
    const char* name = static_cast<const char*>(args);

    while (true)
    {
        // 临界区，只要对临界区加锁，而且加锁的粒度越细越好
        // 加锁的本质是让线程执行临界区代码串行化，不能对所有代码加锁，只能对临界区加锁
        // 加锁是一套规范，通过临界区对临界资源进行访问的时候，要加就都要加
        // 锁保护的是临界区，任何线程执行临界区代码访问临界资源，都必须先申请锁，前提是都必须先看到锁！
        // 这把锁，本身不就也是临界资源吗？锁的设计者早就想到了
        // pthread_mutex_lock: 竞争和申请锁的过程，就是原子的！
        // 难度在加锁的临界区里面，就没有线程切换了吗？？？

        // 加锁
        pthread_mutex_lock(&mutex);
        if (tickets > 0)
        {
            usleep(1000);
            cout << name << " 抢到了票, 票的编号: " << tickets << endl;
            tickets--;

            // 解锁
            pthread_mutex_unlock(&mutex);

            //other code
            usleep(123); //模拟其他业务逻辑的执行
        }
        else
        {
            // 票抢到几张，就算没有了呢？0
            cout << name << "] 已经放弃抢票了，因为没有了..." << endl;
            
            // 解锁
            pthread_mutex_unlock(&mutex);
            break;
        }
    }

    return nullptr;
}

int main()
{
    // 对锁初始化
    pthread_mutex_init(&mutex, nullptr);

    pthread_t tid1;
    pthread_t tid2;
    pthread_t tid3;

    pthread_create(&tid1, nullptr, getTickets, (void*)"thread1");
    pthread_create(&tid2, nullptr, getTickets, (void*)"thread2");
    pthread_create(&tid3, nullptr, getTickets, (void*)"thread3");

    int n = pthread_join(tid1, nullptr);
    cout << n << ":" << strerror(n) << endl;
    pthread_join(tid2, nullptr);
    cout << n << ":" << strerror(n) << endl;
    pthread_join(tid3, nullptr);
    cout << n << ":" << strerror(n) << endl;

    // 锁用完了，释放这把锁
    pthread_mutex_destroy(&mutex);

    return 0;
}
